﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniLua;
using UnityEngine.UI;

public class CombatMgr : MonoBehaviour {

    public CardsMgr CMgr;
    public MonsterMgr MMgr;
    public List<GameObject> PlayerMonsters;
    public List<GameObject> EnemyMonsters;

    private void Awake()
    {
        cbtMgr = this;
        CardsMgr.cbtMgr = this;
    }

    public void Clear()
    {
        CMgr.Clear();
        MMgr.Clear();

        foreach (var go in PlayerMonsters)
            Destroy(go);

        foreach (var go in EnemyMonsters)
            Destroy(go);

        PlayerMonsters.Clear();
        EnemyMonsters.Clear();
    }

    public static LuaTable hCbt = null;
    public static ILuaState ls = null;
    public static int b = 0;
    public void CreateNewCombat(string script)
    {
        tb2Cards.Clear();
        tb2Monster.Clear();
        Clear();

        ls = LuaAPI.NewState();
        ls.L_OpenLibs();
        ls.L_RequireF(LIB_NAME, OpenLib, false);

        var status = ls.L_DoString(@" require ""client_hooks"" require ""tester"" ");

        if (status != ThreadStatus.LUA_OK)
        {
            UniLua.Tools.ULDebug.LogError(ls.L_ToString(-1));
            return;
        }

        if (!ls.IsTable(-1))
        {
            UniLua.Tools.ULDebug.LogError("no combat returned from " + script);
            return;
        }

        // set error handler
        b = ls.GetTop();
        ls.PushCSharpFunction(Traceback);
        ls.Insert(b);

        // get current combat
        ls.GetGlobal("getCombat");
        status = ls.PCall(0, 1, b);
        if (status != ThreadStatus.LUA_OK)
        {
            UniLua.Tools.ULDebug.LogError(ls.L_ToString(-1));
            return;
        }

        hCbt = ls.ToObject(-1) as LuaTable;
        ls.Pop(1);

        // set hooks
        ls.GetGlobal("doHooks");
        ls.PushTable(hCbt);
        status = ls.PCall(1, 0, b);
        if (status != ThreadStatus.LUA_OK)
        {
            UniLua.Tools.ULDebug.LogError(ls.L_ToString(-1));
            return;
        }

        // start the battle
        ls.GetGlobal("battle");
        status = ls.PCall(0, 0, b);
        if (status != ThreadStatus.LUA_OK)
        {
            UniLua.Tools.ULDebug.LogError(ls.L_ToString(-1));
            return;
        }
    }

    static int Traceback(ILuaState ls)
    {
        var msg = ls.ToString(1);
        if (msg != null)
            ls.L_Traceback(ls, msg, 1);
        
        // is there an error object?  
        else if (!ls.IsNoneOrNil(1))
        {
            // try its `tostring' metamethod
            if (!ls.L_CallMeta(1, "__tostring"))
                ls.PushString("(no error message)");
        }

        return 1;
    }

    #region 连接 lua 战斗逻辑

    public void EndPlayerRound()
    {
        ls.PushTable(hCbt);
        ls.GetField(-1, "endPlayerRound");
        ls.PushValue(-2);
        var state = ls.PCall(1, 0, CombatMgr.b);
        if (state != ThreadStatus.LUA_OK)
            UniLua.Tools.ULDebug.LogError(ls.L_ToString(-1));

        ls.Pop(1);
    }

    public static CardsMgr cMgr = null;
    public static CombatMgr cbtMgr = null;
    public static MonsterMgr monMgr = null;
    public const string LIB_NAME = "ClientHooks";
    static int OpenLib(ILuaState lua) // 库的初始化函数  
    {
        var define = new NameFuncPair[]
        {
            new NameFuncPair("OnCardAdded", OnCardAdded),
            new NameFuncPair("OnCardMoved", OnCardMoved),
            new NameFuncPair("OnEnergeChanged", OnEnergeChanged),
            new NameFuncPair("OnMonsterAdded", OnMonsterAdded),
            new NameFuncPair("OnMonsterRemoved", OnMonsterRemoved),
            new NameFuncPair("OnAttrsChanged", OnAttrsChanged),
            new NameFuncPair("OnBuffsChanged", OnBuffsChanged),
            new NameFuncPair("OnPlayerRoundStarted", OnPlayerRoundStarted),
            new NameFuncPair("OnPlayerRoundEnded", OnPlayerRoundEnded),
            new NameFuncPair("OnEnemyRoundStarted", OnEnemyRoundStarted),
            new NameFuncPair("OnEnemyRoundEnded", OnEnemyRoundEnded),
            new NameFuncPair("OnCombatEnded", OnCombatEnded),
        };

        lua.L_NewLib(define);
        return 1;
    }

    static Dictionary<LuaTable, LuaCard> tb2Cards = new Dictionary<LuaTable, LuaCard>();
    static Dictionary<LuaTable, LuaMonster> tb2Monster = new Dictionary<LuaTable, LuaMonster>();

    static int OnCardAdded(ILuaState ls)
    {
        var tb = ls.ToObject(1) as LuaTable;
        var pile = ls.ToString(2);
        var c = new LuaCard(ls as LuaState, tb);
        c.Pile = pile;
        tb2Cards[tb] = c;
        cMgr.AddCard(c, pile);
        return 0;
    }

    static int OnCardMoved(ILuaState ls)
    {
        var tb = ls.ToObject(1) as LuaTable;
        var fromPile = ls.ToString(2);
        var toPile = ls.ToString(3);
        var c = tb2Cards[tb];
        c.Pile = toPile;
        cMgr.MoveCard(c, fromPile, toPile);
        return 0;
    }

    static int OnEnergeChanged(ILuaState ls)
    {
        var g = ls.ToString(1);
        var e = ls.ToInteger(2);

        Debug.Assert(g == "a");
        cbtMgr.EnergeChanged(e);
        return 0;
    }

    static int OnMonsterAdded(ILuaState ls)
    {
        var tb = ls.ToObject(1) as LuaTable;
        var isPlayer = ls.ToBoolean(2);
        var n = ls.ToInteger(3);
        var m = new LuaMonster(ls as LuaState, tb, isPlayer, n);
        monMgr.MonsterAdded(m);
        tb2Monster[tb] = m;
        return 0;
    }

    static int OnMonsterRemoved(ILuaState lua)
    {
        var mtb = ls.ToObject(1) as LuaTable;
        var m = tb2Monster[mtb];
        monMgr.MonsterRemoved(m);
        tb2Monster.Remove(mtb);
        return 0;
    }

    static int OnAttrsChanged(ILuaState ls)
    {
        var m = tb2Monster[ls.ToObject(1) as LuaTable];
        var attr = ls.ToString(2);
        monMgr.AttrChanged(m, attr);
        return 0;
    }

    static int OnBuffsChanged(ILuaState ls)
    {
        var m = tb2Monster[ls.ToObject(1) as LuaTable];
        monMgr.BuffsChanged(m);
        return 0;
    }

    static int OnPlayerRoundStarted(ILuaState ls)
    {
        return 0;
    }

    static int OnPlayerRoundEnded(ILuaState ls)
    {
        return 0;
    }

    static int OnEnemyRoundStarted(ILuaState ls)
    {
        return 0;
    }

    static int OnEnemyRoundEnded(ILuaState ls)
    {
        return 0;
    }

    static int OnCombatEnded(ILuaState ls)
    {
        var winner = ls.ToString(-1);
        Debug.Log(winner + " wins");
        return 0;
    }

    #endregion

    // 能量变化
    public Text EnergeText;
    public void EnergeChanged(int e)
    {
        EnergeText.text = e.ToString();
    }
}
